# Copyright (c) 2011, Chandler Armstrong (omni dot armstrong at gmail dot com)
# see LICENSE.txt for details




"""vizier sprite management and sprite object"""




from __future__ import division
import os

import pygame
from pygame import Rect, sprite, Surface, transform




_IMAGE_CACHE = {}
COLORKEY = (0, 0, 0)



def _set_center(image, (x, y)):
    # move rect center to (x, y) and inflate rect
    # such that moved rect envelopes unmoved rect
    rect = image.get_rect()
    w, h = rect.size
    offset_x, offset_y = abs(x - rect.centerx), abs(y - rect.centery)
    rect.inflate_ip(offset_x * 2, offset_y * 2)
    rect.center = x, y

    surf = Surface(rect.size)
    surf.blit(image, (-rect.x, -rect.y))

    return image


def update_paths(*a, **kw):
    """
    Initialize images from disk.

    arguments:
    *a -- paths to images on disk.

    keyword arguments:
    center -- position to set as center of image, for rotation purposes.

    return list of keys for sprite initialization, keys are in the order
      that the paths were provided to update paths
    """
    set_center = _set_center
    keys = []
    for path in a:
        image, key = load_path(*path)
        if 'center' in kw: image = set_center(image, kw['center'])
        _update(image, key)
        keys.append(key)
    return keys


def update_funcs(*a, **kw):
    """
    Initialize images from functions that return surfaces.

    arguments:
    *a -- lists or tuples of (func, surface[, ...]) where func is a function
      returning a surface, surface is a the surface the function will draw
      upon, and ... are arguments to func

    keyword arguments:
    center -- position to set as center of image, for rotation purposes.

    return list of keys for sprite initialization, keys are in the order
      that the functions were provided to update_funcs
    """
    set_center = _set_center
    keys = []
    for func in a:
        image, key = load_func(*func)
        if 'center' in kw: image = set_center(image, kw['center'])
        _update(image, key)
        keys.append(key)
    return keys


def _update(image, key):
    # update vizier.Sprite image cache
    image_cache = _IMAGE_CACHE
    image_cache[key] = {}
    image_cache[key][0] = image
    for angle in xrange(1, 360):
        image_cache[key][angle] = transform.rotate(image, angle)


def load_path(*a):
    """
    accept a path and load and return a new image from the hard disk

    return None

    this method will not prepare vizier.Sprite to accept the image key
    """
    key = os.path.join(*a)
    image = pygame.image.load(key)
    image = image.convert()
    image.set_colorkey(COLORKEY)
    return image, key


def load_func(func, surface, *a):
    """
    accept a function, surface, and additional arguments and return an image
    produced by the function

    return None

    this method will not prepare vizier.Sprite to accept the image key
    """
    rect = func(surface, *a)
    surface.set_colorkey(COLORKEY)
    key = (func, surface) + a
    return surface, key



class Sprite(sprite.Sprite):
    """
    Base vizier.Sprite object, intended to be subclassed by user designed sprite

    attributes:
    image_key -- the key to the sprite's image in the image_cache.
    image -- sprite image; will change as sprite is zoomed/rotated.
    rect -- sprite rect in world space; see help(Viewer) for details.
    vzrect -- sprite rect in node space; see help(Viewer) for details.
    node -- the node the sprite is on; see help(Viewer) for details.
    P -- a list of points in worldspace, these are converted to nodespace and
      placed in vzP by update1
    vzP -- a list of points in nodespace, these are converted to worldspace and
      placed in P by update0
    angle -- rotation angle
    """


    def __init__(self, image_key, pos, node):
        """
        vizier.Sprite constructor

        arguments:
        image_key -- the key to the sprite's image in the image_cache.
        pos -- the topleft position of the sprite on the node.
        node -- the node the sprite is on; see help(Viewer) for details.
        """
        sprite.Sprite.__init__(self)

        image = _IMAGE_CACHE[image_key][0]
        size = image.get_rect().size

        self.image_key = image_key
        self.image = image
        self.rect = Rect((0, 0), size)
        self.vzrect = Rect(pos, size)
        self.node = node
        self.P = []
        self.vzP = []
        self.angle = 0


    def __getstate__(self):
        odict = self.__dict__.copy()
        del odict['image']
        if isinstance(self.image_key[1], Surface):
            image_key = list(self.image_key)
            image_key[1] = Rect((0, 0), self.image_key[1].get_size())
            odict['image_key'] = image_key
        return odict


    def __setstate__(self, ndict):
        self.__dict__.update(ndict)
        if isinstance(self.image_key[1], Rect):
            self.image_key[1] = Surface(self.image_key[1].size)
            self.image_key = tuple(self.image_key)
            update_funcs(self.image_key)
        else:
            update_paths(self.image_key)
        self.image = _IMAGE_CACHE[self.image_key][0]        


    def rotate(self, angle_delta=0):
        """
        Rotate sprite by angle_delta.

        angle_delta -- change in rotation angle

        return None
        """
        self.angle += angle_delta
        angle = self.angle % 360

        image = _IMAGE_CACHE[self.image_key][angle]
        rect = image.get_rect()
        rect.center = self.vzrect.center

        self.image, self.vzrect = image, rect
